//
//  RxSwiftSequenceViewController.swift
//  DemoProject
//
//  Created by Hao on 2018/8/8.
//  Copyright © 2018年 hao. All rights reserved.
//

import UIKit

/// 特征序列（Traits）：Single、Completable、Maybe、Driver
/// 可以将这些 Traits 看作是 Observable 的另外一个版本。它们之间的区别是：
/// Observable 是能够用于任何上下文环境的通用序列
/// 而 Traits 可以更准确的描述序列。同时它们还提供上下文含义、语法糖
class RxSwiftSequenceViewController: BaseViewController {

    let disposeBag = DisposeBag()
    
    let textField = UITextField(frame: CGRect(x: 0, y: 0, width: 200, height: 34))
    let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 34))
    let button = UIButton(type: .system)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
//        testSequence()
//        testControlPropertyAndEvent()
        testSchedulers()
    }
    
    init() {
        super.init(nibName: nil, bundle: nil)
        testControlPropertyAndEvent()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

//    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
//        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
//    }

}

extension RxSwiftSequenceViewController {
    
    fileprivate func setupView() {
        textField.center = CGPoint(x: MainScreenWidth()*0.5, y: MainScreenHeight()*0.3)
        label.center = CGPoint(x: MainScreenWidth()*0.5, y: MainScreenHeight()*0.4)
        view.addSubview(textField)
        view.addSubview(label)
        
        textField.borderStyle = .roundedRect
        
        button.frame = CGRect(x: 0, y: 0, width: 44, height: 34)
        button.center = CGPoint(x: MainScreenWidth()*0.5, y: MainScreenHeight()*0.55)
        view.addSubview(button)
        button.setTitle("点击", for: .normal)
    }
    
}

/// 调度器(Schedulers)包括:
/// CurrentThreadScheduler：表示当前线程 Scheduler。（默认使用这个）
/// MainScheduler：表示主线程。如果需要执行一些和 UI 相关的任务，就需要切换到该 Scheduler 运行。
/// SerialDispatchQueueScheduler：封装了 GCD 的串行队列。如果需要执行一些串行任务，可以切换到这个 Scheduler 运行。
/// ConcurrentDispatchQueueScheduler：封装了 GCD 的并行队列。如果需要执行一些并发任务，可以切换到这个 Scheduler 运行。
/// OperationQueueScheduler：封装了 NSOperationQueue
// MARK: Schedulers 调度器
extension RxSwiftSequenceViewController {
    
    /// subscribeOn 与 observeOn 区别
    ///    （1）subscribeOn()
    ///        该方法决定数据序列的构建函数在哪个 Scheduler 上运行。
    ///        由于获取数据、解析数据需要花费一段时间的时间，所以通过 subscribeOn 将其切换到后台 Scheduler 来执行。这样可以避免主线程被阻塞。
    ///
    ///    （2）observeOn()
    ///        该方法决定在哪个 Scheduler 上监听这个数据序列。
    ///        解析完毕数据后又通过 observeOn 方法切换到主线程来监听并且处理结果
    fileprivate func testSchedulers() {
        print(info: Thread.current)
        _getPlayList("1")
            // 后台构建序列
            .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
            // 主线程监听并处理序列结果
            .observeOn(MainScheduler.instance)
            .subscribe(onSuccess: { (data) in
                print(info: Thread.current)
                print(info: "网络请求结果: \(data)")
            }) { (error) in
                print(info: Thread.current)
                print(info: "请求发生错误: \(error)")
            }.disposed(by: disposeBag)
    }
    
}

// MARK: 特征序列：Single、Completable、Maybe、Driver
extension RxSwiftSequenceViewController {
    
    // 与数据相关的错误类型
    private enum RxTestDataError: Error {
        case cantParseJSON
    }
    
    //与缓存相关的错误类型
    private enum RxTestCacheError: Error {
        case failedCaching
    }
    
    fileprivate func testSequence() {
//        _single()
//        _asSingle()
//        _completable()
        _maybe()
    }
    
    /// 获取豆瓣某频道下的歌曲信息
    fileprivate func _getPlayList(_ channel: String) -> Single<[String : Any]> {
        return Single<[String : Any]>.create(subscribe: { (single) -> Disposable in
            print(info: Thread.current)
            let url = "https://douban.fm/j/mine/playlist?" + "type=n&channel=\(channel)&from=mainsite"
            let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { (data, _, error) in
                if let error = error {
                    single(.error(error))
                    return
                }
                // guard let <#constant#> = <#expression#> else { return <#return value#> }
                guard let data = data,
                    let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
                    let result = json as? [String: Any] else {
                        single(.error(RxTestDataError.cantParseJSON))
                        return
                }
                single(.success(result))
            })
            task.resume()
            return Disposables.create { task.cancel() }
        })
    }
    
    /// 发出一个元素，或一个 error 事件，不会共享状态变化
    /// 应用场景:
    ///     Single 比较常见的例子就是执行 HTTP 请求，然后返回一个应答或错误
    /// RxSwift 还为 Single 订阅提供了一个枚举（SingleEvent)
    private func _single() {
        // 获取第0个频道的歌曲信息
        _getPlayList("0").subscribe { (event) in
            switch event {
                case .success(let json):
                    print(info: "网络请求结果: \(json)")
                
                case .error(let error):
                    print(info: "请求发生错误: \(error)")
            }
        }.disposed(by: disposeBag)
        
//        _getPlayList("1").subscribe(onSuccess: { (json) in
//            print(info: "网络请求结果: \(json)")
//        }) { (error) in
//            print(info: "请求发生错误: \(error)")
//        }.disposed(by: disposeBag)
    }
    
    /// 通过调用 Observable 序列的 .asSingle() 方法，将它转换为 Single
    /// 运行结果: 当 of() 函数的参数有多个就会报错 error(Sequence contains more than one element.)
    ///     success(1)
    private func _asSingle() {
        /// 还可以使用 asMaybe() 转为 Maybe特征序列
        Observable.of(1).asSingle().subscribe { print(info: $0) }.disposed(by: disposeBag)
    }
    
    private func _cacheLocally() -> Completable {
        return Completable.create(subscribe: { (completable) -> Disposable in
            let isSucces = arc4random()%2 == 0
            guard isSucces else {
                completable(.error(RxTestCacheError.failedCaching))
                return Disposables.create { }
            }
            completable(.completed)
            return Disposables.create()
        })
    }
    
    /// 不会发出任何元素
    /// 只会发出一个 completed 事件或者一个 error 事件
    /// 不会共享状态变化
    /// 应用场景:
    ///     Completable 和 Observable<Void> 有点类似。适用于那些只关心任务是否完成，而不需要在意任务返回值的情况。比如：在程序退出时将一些数据缓存到本地文件，供下次启动时加载。像这种情况我们只关心缓存是否成功
    /// RxSwift 为 Completable 订阅提供了一个枚举（CompletableEvent）
    private func _completable() {
        _cacheLocally().subscribe(onCompleted: {
            print(info: "缓存成功")
        }) { (error) in
            print(info: "缓存失败: \(error.localizedDescription)")
        }.disposed(by: disposeBag)
    }
    
    /// Maybe 介于 Single 和 Completable 之间，它要么只能发出一个元素，要么产生一个 completed 事件，要么产生一个 error 事件
    /// 发出一个元素、或者一个 completed 事件、或者一个 error 事件
    /// 不会共享状态变化
    /// 应用场景:
    ///     Maybe 适合那种可能需要发出一个元素，又可能不需要发出的情况
    /// RxSwift 为 Maybe 订阅提供了一个枚举（MaybeEvent）
    private func _maybe() {
        // 创建 Maybe 和创建 Observable 同样非常相似
        Maybe<String>.create { (maybe) -> Disposable in
            // 订阅者 只会执行一次，执行 success 并不会在执行 completed和error
            maybe(.success("success"))
            maybe(.completed)
            // 失败
            maybe(.error(RxTestDataError.cantParseJSON))
            return Disposables.create()
        }.subscribe { maybe in
            switch maybe {
            case .success(let element):
                print(info: "执行完毕，并获得元素：\(element)")
            case .completed:
                print(info: "执行完毕，且没有任何元素")
            case .error(let error):
                print(info: "执行失败: \(error.localizedDescription)")
            }
        }.disposed(by: disposeBag)
    }
    
    /// 示例: http://www.hangge.com/blog/cache/detail_1942.html
    /// Driver 的目标是提供一种简便的方式在 UI 层编写响应式代码
    /// 如果序列满足如下特征，就可以使用Driver:
    ///     不会产生 error 事件
    ///     一定在主线程监听（MainScheduler）
    ///     共享状态变化（shareReplayLatestWhileConnected）
    private func _driver() {
        /// 例子需求: 根据一个输入框的关键字，来请求数据，然后将获取到的结果绑定到另一个 Label 和 TableView 中
        /// 不使用 Driver 的代码
//        let results = query.rx.text
//            .throttle(0.3, scheduler: MainScheduler.instance)//在主线程中操作，0.3秒内值若多次改变，取最后一次
//            .flatMapLatest { query in //筛选出空值, 拍平序列
//                fetchAutoCompleteItems(query)   //向服务器请求一组结果
//                    .observeOn(MainScheduler.instance)  //将返回结果切换到到主线程上
//                    .catchErrorJustReturn([])       //错误被处理了，这样至少不会终止整个序列
//            }
//            .shareReplay(1)                //HTTP 请求是被共享的
//
//        //将返回的结果绑定到显示结果数量的label上
//        results
//            .map { "\($0.count)" }
//            .bind(to: resultCount.rx.text)
//            .disposed(by: disposeBag)
//
//        //将返回的结果绑定到tableView上
//        results
//            .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
//                cell.textLabel?.text = "\(result)"
//            }
//            .disposed(by: disposeBag)
        
        /// ------------------------------------------------   ///
        /// 由于 drive 方法只能被 Driver 调用。
        /// 如果代码存在 drive，那么这个序列不会产生错误事件并且一定在主线程监听
        /// 就可以安全的绑定 UI 元素
        /// ------------------------------------------------   ///
        
        /** 使用 Driver
         （1）首先我们使用 asDriver 方法将 ControlProperty 转换为 Driver。
         （2）接着我们可以用 .asDriver(onErrorJustReturn: []) 方法将任何 Observable 序列都转成 Driver，因为我们知道序列转换为 Driver 要他满足 3 个条件：
             不会产生 error 事件
             一定在主线程监听（MainScheduler）
             共享状态变化（shareReplayLatestWhileConnected）
         而 asDriver(onErrorJustReturn: []) 相当于以下代码：
             let safeSequence = xs
                 .observeOn(MainScheduler.instance) // 主线程监听
                 .catchErrorJustReturn(onErrorJustReturn) // 无法产生错误
                 .share(replay: 1, scope: .whileConnected)// 共享状态变化
             return Driver(raw: safeSequence) // 封装
         （3）同时在 Driver 中，框架已经默认帮我们加上了 shareReplayLatestWhileConnected，所以我们也没必要再加上"replay"相关的语句了。
         （4）最后记得使用 drive 而不是 bindTo */
//        let results = query.rx.text.asDriver()        // 将普通序列转换为 Driver
//            .throttle(0.3, scheduler: MainScheduler.instance) // 在主线程中操作，0.3秒内值若多次改变，取最后一次
//            .flatMapLatest { query in
//                fetchAutoCompleteItems(query)
//                    .asDriver(onErrorJustReturn: [])  // 仅仅提供发生错误时的备选返回值
//        }
//
//        //将返回的结果绑定到显示结果数量的label上
//        results
//            .map { "\($0.count)" }
//            .drive(resultCount.rx.text) // 这里使用 drive 而不是 bindTo
//            .disposed(by: disposeBag)
//
//        //将返回的结果绑定到tableView上
//        results
//            .drive(resultsTableView.rx.items(cellIdentifier: "Cell")) { //  同样使用 drive 而不是 bindTo
//                (_, result, cell) in
//                cell.textLabel?.text = "\(result)"
//            }
//            .disposed(by: disposeBag)
    }
    
}

// MARK: 特征序列：ControlProperty、 ControlEvent
extension RxSwiftSequenceViewController {
    
    fileprivate func testControlPropertyAndEvent() {
        _controlProperty()
        _controlEvent()
        _customControlEvent()
    }
    
    /// ControlProperty 是专门用来描述 UI 控件属性，拥有该类型的属性都是被观察者（Observable）
    /// ControlProperty 具有以下特征：
    ///    不会产生 error 事件
    ///    一定在 MainScheduler 订阅（主线程订阅）
    ///    一定在 MainScheduler 监听（主线程监听）
    ///    共享状态变化
    private func _controlProperty() {
        // 将textField输入的文字绑定到label上
        textField.rx.text
            .bind(to: label.rx.text)
            .disposed(by: disposeBag)
        /// 自定义 controlProperty
        //        extension UILabel {
        //            public var fontSize: Binder<CGFloat> {
        //                return Binder(self) { label, fontSize in
        //                    label.font = UIFont.systemFont(ofSize: fontSize)
        //                }
        //            }
        //        }
    }
    
    /// ControlEvent 是专门用来描述 UI 控件属性，拥有该类型的属性都是被观察者（Observable）
    /// ControlEvent 具有以下特征：
    ///    不会产生 error 事件
    ///    一定在 MainScheduler 订阅（主线程订阅）
    ///    一定在 MainScheduler 监听（主线程监听）
    ///    共享状态变化
    private func _controlEvent() {
        button.rx.tap.subscribe(onNext: { print(info: "subscribe 点击订阅者回调: \($0)") }).disposed(by: disposeBag)
        button.rx.tap.bind { [weak self] (event) in
            print(info: "bind 点击订阅者回调 -> \(event): \(self!.button)") // \(self.button)
        }.disposed(by: disposeBag)
    }
    
    /// 为 UIViewController 添加 RxSwift 扩展
    private func _customControlEvent() {
        // 页面显示状态完毕
        self.rx.isVisible.subscribe(onNext: { visible in
            print(info: "当前页面显示状态：\(visible)")
        }).disposed(by: disposeBag)
        
        // 页面加载完毕
        self.rx.viewDidLoad.subscribe(onNext: {
            print(info: "viewDidLoad")
        }).disposed(by: disposeBag)
        
        // 页面即将显示
        self.rx.viewWillAppear.subscribe(onNext: { (animated) in
            print(info: "viewWillAppear")
        }).disposed(by: disposeBag)
        
        // 页面显示完毕
        self.rx.viewDidAppear.subscribe(onNext: { (animated) in
            print(info: "viewDidAppear")
        }).disposed(by: disposeBag)
        
        // 页面即将消失
        self.rx.viewWillDisappear.subscribe(onNext: { (animated) in
            print(info: "viewWillDisappear")
        }).disposed(by: disposeBag)
    }
    
}

// MARK: 自定义 ControlEvent, 可直接对 VC 的各种方法进行订阅
fileprivate extension Reactive where Base: RxSwiftSequenceViewController {
    
    fileprivate var viewDidLoad: ControlEvent<Void> {
        let source = self.methodInvoked(#selector(Base.viewDidLoad)).map { _ in }
        return ControlEvent(events: source)
    }
    
    fileprivate var viewWillAppear: ControlEvent<Bool> {
        // self.methodInvoked(#selector(Base.viewWillAppear)).map { $0.first as? Bool ?? false }
        let source = self.methodInvoked(#selector(Base.viewWillAppear)).map { (value) -> Bool in
            value.first as? Bool ?? false
        }
        return ControlEvent(events: source)
    }
    
    fileprivate var viewDidAppear: ControlEvent<Bool> {
        let source = self.methodInvoked(#selector(Base.viewDidAppear))
            .map { $0.first as? Bool ?? false }
        return ControlEvent(events: source)
    }
    
    fileprivate var viewWillDisappear: ControlEvent<Bool> {
        let source = self.methodInvoked(#selector(Base.viewWillDisappear))
            .map { $0.first as? Bool ?? false }
        return ControlEvent(events: source)
    }
    
    fileprivate var viewDidDisappear: ControlEvent<Bool> {
        let source = self.methodInvoked(#selector(Base.viewDidDisappear))
            .map { $0.first as? Bool ?? false }
        return ControlEvent(events: source)
    }
    
    fileprivate var viewWillLayoutSubviews: ControlEvent<Void> {
        let source = self.methodInvoked(#selector(Base.viewWillLayoutSubviews))
            .map { _ in }
        return ControlEvent(events: source)
    }
    
    fileprivate var viewDidLayoutSubviews: ControlEvent<Void> {
        let source = self.methodInvoked(#selector(Base.viewDidLayoutSubviews))
            .map { _ in }
        return ControlEvent(events: source)
    }
    
    fileprivate var willMoveToParentViewController: ControlEvent<UIViewController?> {
        let source = self.methodInvoked(#selector(Base.willMove))
            .map { $0.first as? UIViewController }
        return ControlEvent(events: source)
    }
    
    fileprivate var didMoveToParentViewController: ControlEvent<UIViewController?> {
        let source = self.methodInvoked(#selector(Base.didMove))
            .map { $0.first as? UIViewController }
        return ControlEvent(events: source)
    }
    
    fileprivate var didReceiveMemoryWarning: ControlEvent<Void> {
        let source = self.methodInvoked(#selector(Base.didReceiveMemoryWarning))
            .map { _ in }
        return ControlEvent(events: source)
    }
    
    /// 表示视图是否显示的可观察序列，当VC显示状态改变时会触发
    fileprivate var isVisible: Observable<Bool> {
//        let viewDidAppearObservable = self.base.rx.viewDidAppear.map { _ in true }
        let viewDidAppearObservable = self.base.rx.viewDidAppear.map { (value) -> Bool in
            print(info: "viewDidAppearObservable -> value: \(value)")
            return true
        }
        let viewWillDisappearObservable = self.base.rx.viewWillDisappear.map { _ in false }
        return Observable<Bool>.merge(viewDidAppearObservable, viewWillDisappearObservable)
    }
    
    /// 表示页面被释放的可观察序列，当VC被dismiss时会触发
    fileprivate var isDismissing: ControlEvent<Bool> {
        let source = self.sentMessage(#selector(Base.dismiss)).map { $0.first as? Bool ?? false }
        return ControlEvent(events: source)
    }
    
}

